/*
 * perceptron.h
 *
 *  Created on: Feb 28, 2011
 *      Author: tqlong
 */

#ifndef PERCEPTRON_H_
#define PERCEPTRON_H_

#include "data/dataset_tpl.h"

/** Online learning algorithms */
template <typename T>
class LearningAlgorithm {
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;

  double predict_;

  /** Set parameter of the algorithms */
  virtual void setParameters(const std::vector<double>& params) = 0;

  /** Online update using 1 sample/label, use this after predict() */
  virtual double update(const vec_type& x, double y, double p) = 0;

  /** Class prediction */
  virtual double predict(const vec_type& x) = 0;

  /** Print result */
  virtual void print() const = 0;
};

/** Batch learning algorithms */
template <typename T>
class BatchLearningAlgorithm : public LearningAlgorithm<T> {
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;
  typedef LearningAlgorithm<T> __Base;

  /** Do nothing on 1 sample (no online update) */
  double update(const vec_type& x, double y, double p) { return 0; }

  /** Training using a set of samples (with label) */
  virtual double update(const dataset_type& data) = 0;
};

template <typename T>
class Perceptron : public LearningAlgorithm<T>
{
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;
  typedef LearningAlgorithm<T> __Base;

  /** Weight vector */
  vec_type w_;
  /** Learning rate */
  double alpha_;

  Perceptron(int d = 1) : w_(d), alpha_(1.0) { w_.randn(); }

  /** Set alpha_ */
  void setParameters(const std::vector<double>& params);
  double update(const vec_type& x, double y, double p);
  double predict(const vec_type& x);
  void print() const { std::cout << w_; }
};

template <typename T>
class WeightedMajority : public LearningAlgorithm<T>
{
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;
  typedef LearningAlgorithm<T> __Base;

  std::vector<LearningAlgorithm<T>*> expert_;
  std::vector<double> pred_;
  std::vector<double> weight_;
  double beta_;

  WeightedMajority() : beta_(0.5) {}

  // implement virtual members
  void setParameters(const std::vector<double>& params);
  double update(const vec_type& x, double y, double p);
  double predict(const vec_type& x);
  void print() const;

  // other members
  void add(LearningAlgorithm<T>& algo);
  int n_expert() const { return expert_.size(); }
};

template <typename T>
class L1LossL2Reg : public BatchLearningAlgorithm<T>
{
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;
  typedef BatchLearningAlgorithm<T> __Base;

  /** Weight vector */
  vec_type w_;
  /** Learning rate */
  double alpha_;
  /** Regularization parameter */
  double C_;
  /** Maximum number of iteration */
  int maxiter_;

  /** Alpha (support coefficient) */
  vec_type a_;

protected:
  /** Qii = <xi,xi>+Dii, Dii = 0 | .5/C */
  vec_type Qii_, Dii_;
  /** U = C | inf */
  double U_;

public:
  L1LossL2Reg(int d = 1) : w_(d), alpha_(1.0), C_(100.0), maxiter_(100) { w_.randn(); }
  void setParameters(const std::vector<double>& params);
  double update(const dataset_type& data);
  double predict(const vec_type& x);
  void iterate(const dataset_type& data);
  virtual void prepareQD(const dataset_type& data);
  void print() const { std::cout << "w = " << w_ << "\nalpha = " << a_; }
};

template <typename T>
class L2LossL2Reg : public L1LossL2Reg<T>
{
public:
  typedef T               vec_type;
  typedef Example<T>      example_type;
  typedef Dataset<T>      dataset_type;
  typedef L1LossL2Reg<T> __Base;

  L2LossL2Reg(int d = 1) : L1LossL2Reg<T>(d) {}
  void prepareQD(const dataset_type& data);
};

class Compare
{
public:
  double tol_;
  Compare(double tol = 1e-10) : tol_(tol) {}
  bool equal(double x, double y) { return fabs(x - y) < tol_; }
  bool less(double x, double y) { return x < y-tol_; }
  bool greater(double x, double y) { return x > y+tol_; }
  bool lesseq(double x, double y) { return x <= y + tol_; }
  bool greatereq(double x, double y) { return x >= y-tol_; }
};

#include "perceptron_impl.h"
#include "weightedmajority_impl.h"
#include "l2regSVM_impl.h"

#endif /* PERCEPTRON_H_ */
